package cn.sylinx.horm.resource.lexer;

import java.text.CharacterIterator;
import java.text.StringCharacterIterator;

/**
 * sql 词法分析
 * 
 * @author johnhan
 *
 */
public class SqlLexer {

    private static final char ESCAPE_SYMBOL = '\\'; // 转义符
    private static final char SQUARE_BRACKET_RIGHT = ']'; // 右中括号
    private static final char SQUARE_BRACKET_LEFT = '['; // 左中括号
    private static final char C_BLANK = ' '; // 字符空格
    private static final char ES_T = '\t'; // 转义字符\t
    private static final char ES_R = '\r'; // 转义字符\r
    private static final char ES_N = '\n'; // 转义字符\n
    private static final char KEYWORD_BEGIN = '#'; // 关键字开头

    private final String unparsedSql;
    private final CharacterIterator iterator;

    private char peek;
    private int lineNo;

    public SqlLexer(final String unparsedSql) {
        this.iterator = new StringCharacterIterator(unparsedSql);
        this.unparsedSql = unparsedSql;
        this.peek = this.iterator.current();
        this.lineNo = 1;
    }

    public Token scan() {

        for (;; nextChar()) {

            if (isEnd()) {
                return null;
            }

            // 忽略空白、换行、tab
            if (this.peek == C_BLANK || this.peek == ES_T || this.peek == ES_R || this.peek == ES_N) {
                if (this.peek == ES_N) {
                    this.lineNo++;
                }
                continue;
            }

            break;
        }

        if (this.peek == KEYWORD_BEGIN) {
            // 遇到#
            int startIndex = this.iterator.getIndex();
            nextChar(); // skip '#'

            StringBuilder sb = new StringBuilder();
            sb.append(KEYWORD_BEGIN).append(this.peek);

            do {
                String content = sb.toString();
                if (hitKeyword(content)) {
                    // 是关键字
                    nextChar();

                    if (needSquareBracketContent(content)) {
                        // 如果必须有内容，还需后续条件
                        if (this.peek != SQUARE_BRACKET_LEFT) {
                            return scan();
                        }

                        // 后续解析条件内容
                        String squareBracketContent = parseSquareBracketContent();
                        KeywordToken token = Keyword.create(content, lineNo, startIndex, iterator.getIndex());
                        token.setContent(squareBracketContent);
                        return token;

                    } else {

                        // 无需条件，判断后面得符号是否合法
                        if (this.peek == C_BLANK || this.peek == ES_T || this.peek == ES_R || this.peek == ES_N
                                || isEnd()) {
                            return Keyword.create(content, lineNo, startIndex, iterator.getIndex());
                        }

                        return scan();
                    }

                } else {

                    nextChar();
                    sb.append(this.peek);
                }

            } while (isSeeminglyKeyword(sb.toString()));

        }

        nextChar();
        return scan();
    }

    private String parseSquareBracketContent() {

        StringBuilder sb = new StringBuilder();
        nextChar();
        boolean closed = false;

        for (;; nextChar()) {

            if (isEnd()) {
                break;
            }

            if (isEscapeSymbol()) {
                // 转义符，看后面是否针对]转义
                nextChar();

                if (isSquareBracketsRight()) {
                    sb.append(SQUARE_BRACKET_RIGHT);
                } else {
                    sb.append(ESCAPE_SYMBOL).append(peek);
                }

            } else {

                if (isSquareBracketsRight()) {
                    nextChar();
                    closed = true;
                    break;
                }

                sb.append(peek);
            }
        }

        if (!closed) {
            throw new RuntimeException("invalid condtion, right square bracket missed");
        }

        String r = sb.toString();
        if (r.trim().isEmpty()) {
            throw new RuntimeException("empty condition");
        }

        return sb.toString();
    }

    private boolean isSquareBracketsRight() {
        return peek == SQUARE_BRACKET_RIGHT;
    }

    private boolean isEscapeSymbol() {
        return peek == ESCAPE_SYMBOL;
    }

    public boolean isEnd() {
        return this.peek == CharacterIterator.DONE;
    }

    private boolean needSquareBracketContent(String key) {
        return Keyword.needSquareBracketContent(key);
    }

    private boolean hitKeyword(String key) {
        return Keyword.hitKeyword(key);
    }

    private boolean isSeeminglyKeyword(String kw) {
        return Keyword.isSeeminglyKeyword(kw);
    }

    private void nextChar() {
        this.peek = this.iterator.next();
    }

    public char getPeek() {
        return peek;
    }

    public int getLineNo() {
        return lineNo;
    }

    public String getUnparsedSql() {
        return unparsedSql;
    }

    public static void main(String[] args) {

        String unparsedSql = "#IF[a > 0] aaaa #ELSE bbb #{testField} #FUNC[a,xx,bb] #END";
        SqlLexer sqlLexer = new SqlLexer(unparsedSql);

        while (!sqlLexer.isEnd()) {
            Token token = sqlLexer.scan();
            System.out.println(token);
            System.out.println(unparsedSql.substring(token.getStartIndex(), token.getEndIndex()));
        }
    }

}
